package com.mdd.front.service.recharge.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.mdd.common.core.PageResult;
import com.mdd.common.entity.account.AccountLog;
import com.mdd.common.entity.log.OrderLog;
import com.mdd.common.entity.order.Order;
import com.mdd.common.entity.recharge.RechargeOrder;
import com.mdd.common.entity.user.User;
import com.mdd.common.enums.*;
import com.mdd.common.exception.OperateException;
import com.mdd.common.mapper.account.AccountLogMapper;
import com.mdd.common.mapper.recharge.RechargeOrderMapper;
import com.mdd.common.mapper.user.UserMapper;
import com.mdd.common.utils.ConfigUtil;
import com.mdd.common.utils.SnUtils;
import com.mdd.common.utils.TimeUtil;
import com.mdd.common.utils.WxPayServiceUtils;
import com.mdd.front.LikeFrontThreadLocal;
import com.mdd.front.service.log.IOrderLogService;
import com.mdd.front.service.recharge.IRechargeService;
import com.mdd.front.validate.RechargeValidate;
import com.mdd.front.validate.common.PageValidate;
import com.mdd.front.vo.recharge.RechargeConfigVo;
import com.mdd.front.vo.recharge.RechargeRecordVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class RechargeServiceImpl implements IRechargeService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RechargeOrderMapper rechargeOrderMapper;

    @Resource
    private IOrderLogService iOrderLogService;

    @Resource
    private AccountLogMapper accountLogMapper;
    
    @Override
    public RechargeConfigVo config(Integer userId) {
        User user = userMapper.selectById(userId);
        Assert.notNull(user, "用户不存在！");
        Map<String, String> config = ConfigUtil.get("recharge");
        RechargeConfigVo configVo = new RechargeConfigVo();
        configVo.setOpenRecharge(Integer.parseInt(config.getOrDefault("rechargeOpen","0")));
        configVo.setMinRechargeMoney(new BigDecimal(config.getOrDefault("minRechargeAmount","0")));
        configVo.setUserMoney(user.getMoney());
        return configVo;
    }

    /**
     * 充值记录
     *
     * @param userId 用户ID
     * @param pageValidate 分页参数
     * @return PageResult<RechargeRecordVo>
     */
    @Override
    public PageResult<RechargeRecordVo> record(Integer userId, PageValidate pageValidate) {
        Integer pageNo   = pageValidate.getPageNo();
        Integer pageSize = pageValidate.getPageSize();

        QueryWrapper<RechargeOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        queryWrapper.eq("pay_status", PaymentEnum.OK_PAID.getCode());
        queryWrapper.orderByDesc("id");

        IPage<RechargeOrder> iPage = rechargeOrderMapper.selectPage(new Page<>(pageNo, pageSize), queryWrapper);

        List<RechargeRecordVo> list = new LinkedList<>();
        for (RechargeOrder rechargeOrder : iPage.getRecords()) {
            RechargeRecordVo vo = new RechargeRecordVo();
            vo.setId(rechargeOrder.getId());
            vo.setAction(1);
            vo.setOrderAmount(rechargeOrder.getOrderAmount());
            vo.setCreateTime(TimeUtil.timestampToDate(rechargeOrder.getPayTime()));
            vo.setTips("充值" + vo.getOrderAmount() + "元");
            vo.setDesc(AccountEnum.UM_INC_RECHARGE.getMsg());
            list.add(vo);
        }

        return PageResult.iPageHandle(iPage.getTotal(), iPage.getCurrent(), iPage.getSize(), list);
    }

    /**
     * 创建充值订单
     *
     * @author fzr
     * @param userId 用户ID
     * @param terminal 设备端
     * @param rechargeValidate 参数
     * @return Map<String, Object>
     */
    @Override
    public Map<String, Object> placeOrder(Integer userId, Integer terminal, RechargeValidate rechargeValidate) {
        RechargeConfigVo config = this.config(userId);
        if (config.getOpenRecharge().equals(0)) {
            throw new OperateException("充值功能已关闭");
        }
        if (rechargeValidate.getOrderAmount().compareTo(config.getMinRechargeMoney()) < 0) {
            throw new OperateException("充值金额不能少于" + config.getMinRechargeMoney());
        }
        RechargeOrder order = new RechargeOrder();
        order.setUserId(userId);
        order.setOrderTerminal(terminal);
        order.setOrderSn(rechargeOrderMapper.randMakeOrderSn("order_sn"));
        order.setPayStatus(0);
        order.setRefundStatus(0);
        order.setOrderAmount(rechargeValidate.getOrderAmount());
        order.setCreateTime(System.currentTimeMillis() / 1000);
        order.setUpdateTime(System.currentTimeMillis() / 1000);
        order.setDeleteTime(System.currentTimeMillis() / 1000);
        rechargeOrderMapper.insert(order);
        Map<String, Object> response = new LinkedHashMap<>();
        response.put("orderId", order.getId());
        return response;
    }

    private void saveOrderLog(Long orderId, Long userId, String context) {
        OrderLog orderLog = new OrderLog();
        orderLog.setType(OperateEnum.TYPE_USER.getType());
        //orderLog.setChannel(0);
        orderLog.setOrderId(orderId);
        orderLog.setOperatorId(userId);
        orderLog.setContent(context);
        orderLog.setCreateTime(TimeUtil.timestamp());
        orderLog.setUpdateTime(TimeUtil.timestamp());
        iOrderLogService.saveEntity(orderLog);
    }

    @Override
    public boolean updateOrderStatusToPaid(String outTradeNo, String transactionId) throws WxPayException {
        //处理重复的通知
        //接口调用的幂等性：无论接口被调用多少次，产生的结果是一致的。
        LambdaQueryWrapper<RechargeOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(RechargeOrder::getOrderSn, outTradeNo);
        lambdaQueryWrapper.last("limit 1");
        RechargeOrder rechargeOrder = rechargeOrderMapper.selectOne(lambdaQueryWrapper);
        Assert.notNull(rechargeOrder, "订单未关联到对应的订单!");
        Integer payStatus = rechargeOrder.getPayStatus();
        if (OrderPayStatusEnum.WAIT_PAY.getStatus() != payStatus) {
            return true;
        }
        WxPayOrderQueryV3Result wxPayOrderQueryV3Result = WxPayServiceUtils.wxPayService(rechargeOrder.getPayChannel()).queryOrderV3(transactionId, outTradeNo);
        String tradeState = wxPayOrderQueryV3Result.getTradeState();
        transactionId = wxPayOrderQueryV3Result.getTransactionId();
        if ("SUCCESS".equals(tradeState)) {
            rechargeOrder.setTransactionId(transactionId);
            //更新订单状态
            rechargeOrder.setPayStatus(OrderPayStatusEnum.PAY.getStatus());
            rechargeOrder.setPayTime(TimeUtil.timestamp());
            rechargeOrder.setPayWay(OrderPlayTypeEnum.WECHAT_PAY.getType());
            rechargeOrder.setUpdateTime(TimeUtil.timestamp());
            rechargeOrderMapper.updateById(rechargeOrder);
            //记录支付日志
            this.saveOrderLog(Long.valueOf(rechargeOrder.getId()), Long.valueOf(rechargeOrder.getUserId()), OrderLogEnum.USER_PAY_ORDER.getDesc());
            return true;
        }
        return false;
    }

    @Override
    public RechargeOrder updateSn(Integer id, String payChannel) {
        RechargeOrder rechargeOrder = rechargeOrderMapper.selectById(id);
        org.springframework.util.Assert.notNull(rechargeOrder, "订单不存在");
        rechargeOrder.setOrderSn(SnUtils.getOrderSn());
        rechargeOrder.setPayChannel(payChannel);
        rechargeOrder.setPayWay(OrderPlayTypeEnum.WECHAT_PAY.getType());
        this.rechargeOrderMapper.updateById(rechargeOrder);
        return rechargeOrder;
    }

    @Override
    public RechargeOrder payStatus(Long id) throws Exception {
        RechargeOrder rechargeOrder = rechargeOrderMapper.selectOne(new QueryWrapper<RechargeOrder>()
                .eq("id", id)
                .last("limit 1"));
        Assert.notNull(rechargeOrder, "订单不存在！");
        checkOrderStatus(id);
        return rechargeOrder;
    }

    @Override
    public void checkOrderStatus(Long orderId) throws Exception {
        log.warn("根据订单号核实订单状态 ===> {}", orderId);
        //调用微信支付查单接口
        RechargeOrder model = rechargeOrderMapper.selectOne(
                new QueryWrapper<RechargeOrder>()
                        .eq("id", orderId)
                        .last("limit 1"));
        if (OrderPayStatusEnum.PAY.getStatus() == model.getPayStatus()) {
            return;
        }
        WxPayOrderQueryV3Result queryOrderV3 = WxPayServiceUtils.wxPayService(model.getPayChannel()).queryOrderV3(model.getTransactionId(), model.getOrderSn());
        String tradeState = queryOrderV3.getTradeState();
        //判断订单状态
        if ("SUCCESS".equals(tradeState)) {
            // 修改订单的支付信息
            model.setPayTime(TimeUtil.dateToTimestamp(TimeUtil.getStringByRFC3339(queryOrderV3.getSuccessTime())));
            //model.setOrderStatus(OrderStatusEnum.RESERVE.getStatus());
            model.setPayStatus(OrderPayStatusEnum.PAY.getStatus());
            model.setPayWay(OrderPlayTypeEnum.WECHAT_PAY.getType());
            model.setTransactionId(queryOrderV3.getTransactionId());
            model.setUpdateTime(TimeUtil.timestamp());
            rechargeOrderMapper.updateById(model);
            User user = userMapper.selectOne(new QueryWrapper<User>()
                    .eq("id", model.getUserId())
                    .eq("is_delete", 0)
                    .last("limit 1"));
            user.setMoney(user.getMoney().add(model.getOrderAmount()));
            userMapper.updateById(user);
            // 记录日志
            this.saveOrderLog(orderId, Long.valueOf(model.getUserId()), OrderLogEnum.USER_PAY_ORDER.getDesc());
            // 记录余额明细
            this.rechargeAccountLog(orderId, model.getOrderAmount(), user.getMoney());
        } else if ("CLOSE".equals(tradeState)) {
            log.warn("核实订单已关闭 ===> {}", model.getOrderSn());
        } else {
            log.warn("核实订单未支付 ===> {}", model.getOrderSn());
            //如果订单未支付，则调用关单接口
            //this.cancelPay(model.getId());
            WxPayServiceUtils.wxPayService(model.getPayChannel()).closeOrderV3(model.getOrderSn());
            //更新本地订单状态
            //model.setOrderStatus(OrderStatusEnum.CLOSE.getStatus());
            model.setCancelTime(TimeUtil.timestamp());
            rechargeOrderMapper.updateById(model);
            this.saveOrderLog(orderId, Long.valueOf(model.getUserId()), OrderLogEnum.SYSTEM_CANCEL_ORDER.getDesc());
        }
    }

    @Override
    public void updateRechargePayWay(Integer orderId, Integer payWay) {
        LambdaQueryWrapper<RechargeOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(RechargeOrder::getId, orderId);
        lambdaQueryWrapper.eq(RechargeOrder::getUserId, LikeFrontThreadLocal.getUserId());
        lambdaQueryWrapper.eq(RechargeOrder::getPayStatus, OrderStatusEnum.WAIT_PAY.getStatus());
        lambdaQueryWrapper.eq(RechargeOrder::getPayStatus, OrderPayStatusEnum.WAIT_PAY.getStatus());
        lambdaQueryWrapper.last("limit 1");
        RechargeOrder rechargeOrder = rechargeOrderMapper.selectOne(lambdaQueryWrapper);
        Assert.notNull(rechargeOrder,"订单不存在或不处于待支付状态！");
        rechargeOrder.setPayWay(payWay);
        rechargeOrder.setUpdateTime(System.currentTimeMillis() / 1000);
        rechargeOrderMapper.updateById(rechargeOrder);
    }

    public void rechargeAccountLog(Long orderId, BigDecimal changeAmount, BigDecimal leftAmount){
        AccountLog accountLog = new AccountLog();
        accountLog.setUserId(Long.valueOf(LikeFrontThreadLocal.getUserId()));
        accountLog.setAction(AccountChangeEnum.ACTION_INC.getCode());
        accountLog.setAssociationSn("-");
        accountLog.setLeftAmount(leftAmount);
        accountLog.setChangeType(AccountEnum.UM_INC_RECHARGE.getCode());
        accountLog.setChangeAmount(changeAmount);
        accountLog.setCreateTime(TimeUtil.timestamp());
        accountLog.setUpdateTime(TimeUtil.timestamp());
        accountLogMapper.insert(accountLog);
    }

    public void rechargeAccountLog(Long orderId, BigDecimal changeAmount, BigDecimal leftAmount, Long userId){
        AccountLog accountLog = new AccountLog();
        accountLog.setUserId(userId);
        accountLog.setAction(AccountChangeEnum.ACTION_INC.getCode());
        accountLog.setAssociationSn("-");
        accountLog.setLeftAmount(leftAmount);
        accountLog.setChangeType(AccountEnum.UM_INC_RECHARGE.getCode());
        accountLog.setChangeAmount(changeAmount);
        accountLog.setCreateTime(TimeUtil.timestamp());
        accountLog.setUpdateTime(TimeUtil.timestamp());
        accountLogMapper.insert(accountLog);
    }

    @Override
    public boolean updateAliOrderStatusToPaid(String outTradeNo, String transactionId) {
        //处理重复的通知
        //接口调用的幂等性：无论接口被调用多少次，产生的结果是一致的。
        LambdaQueryWrapper<RechargeOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(RechargeOrder::getOrderSn, outTradeNo);
        lambdaQueryWrapper.last("limit 1");
        RechargeOrder rechargeOrder = rechargeOrderMapper.selectOne(lambdaQueryWrapper);
        Assert.notNull(rechargeOrder, "订单未关联到对应的订单");
        Integer payStatus = rechargeOrder.getPayStatus();
        if (OrderPayStatusEnum.WAIT_PAY.getStatus() != payStatus) {
            return true;
        }
        rechargeOrder.setTransactionId(transactionId);
        //更新订单状态
        rechargeOrder.setPayStatus(OrderPayStatusEnum.PAY.getStatus());
        rechargeOrder.setPayTime(TimeUtil.timestamp());
        rechargeOrder.setPayWay(OrderPlayTypeEnum.ALI_PAY.getType());
        rechargeOrder.setUpdateTime(TimeUtil.timestamp());
        rechargeOrderMapper.updateById(rechargeOrder);

        User user = userMapper.selectOne(new QueryWrapper<User>()
                .eq("id", rechargeOrder.getUserId())
                .eq("is_delete", 0)
                .last("limit 1"));
        user.setMoney(user.getMoney().add(rechargeOrder.getOrderAmount()));
        userMapper.updateById(user);

        //记录支付日志
        this.saveOrderLog(Long.valueOf(rechargeOrder.getId()), Long.valueOf(rechargeOrder.getUserId()), OrderLogEnum.USER_PAY_ORDER.getDesc());

        // 记录余额明细
        this.rechargeAccountLog(Long.valueOf(rechargeOrder.getId()), rechargeOrder.getOrderAmount(), user.getMoney(), Long.valueOf(rechargeOrder.getUserId()));

        return true;
    }
}
